`summarise()` has grouped output by 'test_description'. You can override using
the `.groups` argument.
test_description
Frequent Tools
n
CAARS-S:L
CAARS
5
ASRS-A
ASRS
4
BAARS-IV
BAARS
3
ASRS-v1.1
ASRS
2
ADHD-items
Other
1
ADSA
Other
1
AHA
Other
1
ALS-SF
Other
1
APQ
Other
1
ASRS
ASRS
1
ASRS-5
ASRS
1
ASSET-BS
Other
1
BADDS
Other
1
CAARS-AI
CAARS
1
CAARS-S
CAARS
1
CAARS-S:SV
CAARS
1
CBS
Other
1
EarlyDetect(ASRS)
Other
1
IPDE-SQ-11
Other
1
Online
Other
1
PAI
Other
1
PDI-4
Other
1
SR-WRAADDS
Other
1
WURS
WURS
1
WURS-25
WURS
1
WURS-4
WURS
1
WURS-61
WURS
1
WURS/CAARS
WURS
1
# 2. Inspect your data (optional, but highly recommended for debugging)# print(str(plot_data))# print(summary(plot_data$size))# print(sum(is.na(plot_data$size)))# print(range(plot_data$size, na.rm = TRUE))# 1. Define your colors in a named vector# You must assign a color to every category, or the others will disappear/turn grey.my_colors <-c("ASRS"="red", # Replace with your preferred color"CAARS"="blue", # Replace with your preferred color"BAARS"="darkgreen", # Replace with your preferred color"WURS"="brown4", # Replace with your preferred color"Other"="black"# The specific requirement)# 2. Plotggplot2::ggplot(data = plot_data, aes(x = sensitivity,y = specificity,shape = Neurotypical_Only,size = size,color =`Frequent Tools` )) +geom_point() +# 3. Add the manual scalescale_color_manual(values = my_colors) +xlab("Sensitivity (%)") +ylab("Specificity (%)") + ggrepel::geom_text_repel(aes(label = test_description), max.overlaps =10, size =3) +labs(shape ="Neurotypical only" ) +scale_size_continuous(name ="Size", # This sets the legend titlebreaks =c(50, 500, 1000), # Original untransformed values for legendlabels =c("50", "500", "1000"), # Corresponding labelsguide ="legend", # Explicitly request a legend# Set limits based on the *transformed* size values from your plot_data# This is crucial for the scale to match your actual plotted points ) +theme(legend.position ="right")
The following object is masked from 'package:dplyr':
group_rows
# Function to read CSV and select specific columnsread_adhd_data <-function(file_path) {# Read the CSV file data <-read.csv(file_path)# Select only the required columns clean_data <- data %>%select(n_ADHD, sensitivity_self_report, specificity_self_report, size, ID)return(clean_data)}########################### Usage exampleaccuracy <-read_adhd_data(data_location)############################## Check the resulthead(accuracy)
n_ADHD sensitivity_self_report specificity_self_report size
1 518 NA NA 845
2 38 NA NA 69
3 142 92 NA 280
4 1006 NA NA 1135
5 63 89 13 69
6 70 81 71 140
ID
1 Abramson, 2023{#5}
2 Adamou, 2022{#8}
3 Aita, 2018{#16}
4 Amen, 2021{#28}
5 Bakare, 2020{#45}
6 Bastiaens, 2017{#55}
n_ADHD sensitivity_self_report specificity_self_report size
1 518 NA NA 845
2 38 NA NA 69
3 142 92 NA 280
4 1006 NA NA 1135
5 63 89 13 69
6 70 81 71 140
ID Prevalence One_Minus_Prevalence Sensitivity Specificity
1 Abramson, 2023{#5} 0.6130178 0.38698225 NA NA
2 Adamou, 2022{#8} 0.5507246 0.44927536 NA NA
3 Aita, 2018{#16} 0.5071429 0.49285714 92 NA
4 Amen, 2021{#28} 0.8863436 0.11365639 NA NA
5 Bakare, 2020{#45} 0.9130435 0.08695652 89 13
6 Bastiaens, 2017{#55} 0.5000000 0.50000000 81 71
Accuracy
1 NA
2 NA
3 NA
4 NA
5 82.3913
6 76.0000
#install.packages("kableExtra")accuracy <- accuracy %>%select(-Sensitivity, -Specificity)# Create and save in one pipe chainoutput_file <-"accuracy.html"# Create and savekableExtra::kbl(accuracy) %>%kable_styling(bootstrap_options =c("striped", "hover")) %>%save_kable(file = output_file)
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `admin_combination = as.numeric(admin_combination)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_combination"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "ICC_combination"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_combination"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `cost_combination = as.numeric(cost_combination)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_combination"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `concordance_combination = as.numeric(concordance_combination)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_self.report"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `cost_self.report = as.numeric(cost_self.report)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `admin_peer_rating = as.numeric(admin_peer_rating)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_peer_rating"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "ICC_peer_rating"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_peer_rating"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `cost_peer_rating = as.numeric(cost_peer_rating)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_peer_rating"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `concordance_peer_rating = as.numeric(concordance_peer_rating)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `admin_neuroimaging = as.numeric(admin_neuroimaging)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_neuroimaging"
[1] "ICC_neuroimaging"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_neuroimaging"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `cost_neuroimaging = as.numeric(cost_neuroimaging)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_neuroimaging"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `concordance_neuroimaging =
as.numeric(concordance_neuroimaging)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `admin_EEG = as.numeric(admin_EEG)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_EEG"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "ICC_EEG"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_EEG"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `cost_EEG = as.numeric(cost_EEG)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_EEG"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `concordance_EEG = as.numeric(concordance_EEG)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `admin_biomarker = as.numeric(admin_biomarker)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_biomarker"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "ICC_biomarker"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_biomarker"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `cost_biomarker = as.numeric(cost_biomarker)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_biomarker"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `concordance_biomarker = as.numeric(concordance_biomarker)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "clinical_misdiagnosis_observational"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "sensitivity_observational"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "specificity_observational"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "admin_observational"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_observational"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "ICC_observational"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_observational"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_observational"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `admin_clinician_interview =
as.numeric(admin_clinician_interview)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_clinician_interview"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "ICC_clinician_interview"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_clinician_interview"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `cost_clinician_interview =
as.numeric(cost_clinician_interview)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_clinician_interview"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `concordance_clinician_interview =
as.numeric(concordance_clinician_interview)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "clinical_misdiagnosis_clinician_tool"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "sensitivity_clinician_tool"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "specificity_clinician_tool"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "admin_clinician_tool"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_clinician_tool"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "ICC_clinician_tool"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_clinician_tool"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_clinician_tool"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `admin_feigningADHD = as.numeric(admin_feigningADHD)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "kappa_feigningADHD"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "ICC_feigningADHD"
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "cost_feigningADHD"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `cost_feigningADHD = as.numeric(cost_feigningADHD)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
[1] "concordance_feigningADHD"
Warning: There was 1 warning in `dplyr::mutate()`.
ℹ In argument: `concordance_feigningADHD =
as.numeric(concordance_feigningADHD)`.
Caused by warning:
! NAs introduced by coercion
Warning in min(d_now[[test_outcome_and_result]]): no non-missing arguments to
min; returning Inf
Warning in max(d_now[[test_outcome_and_result]]): no non-missing arguments to
max; returning -Inf
# If you want to clean up any trailing spaces left behind:d <- d %>% dplyr::mutate(study =gsub("\\{.*\\}", "", ID),study =trimws(study),study =ifelse(study =="van de Glind, 2013", "Van de Glind, 2013", study) )d$instrument_self_report <-word(d$test_description_self, 1)# Calculate Logit Sensitivitydat_sens <- metafor::escalc(measure="PLO", xi=true_positive_self_report, ni=(true_positive_self_report + false_negative_self_report), data=d, add=0.5)dat_sens$outcome <-"sens"dat_sens <- dat_sens %>% dplyr::arrange(desc(study))# Calculate Logit Specificitydat_spec <- metafor::escalc(measure="PLO", xi=true_negative_self_report, ni=(true_negative_self_report + false_positive_self_report), data=d, add=0.5)dat_spec$outcome <-"spec"dat_spec <- dat_spec %>% dplyr::arrange(desc(study))# Combine into long formatdat_long <-rbind(dat_sens, dat_spec)dat_long <- dat_long[order(dat_long$study), ] # Group by studyall_studies <- dat_long %>%group_by(study, instrument_self_report) %>%# Keep studies where at least one row (sens or spec) is not NAfilter(any(!is.na(yi))) %>%ungroup() %>%# Get the unique names and sort them alphabeticallydistinct(study) %>%arrange(desc(study)) %>%# Assign the fixed row numbers for the plotsmutate(row_num =row_number())res <- metafor::rma.mv( yi, vi, mods =~ outcome -1, # Separate means for sens and specrandom =~ outcome | study, # Random effects for outcomes within studiesstruct ="UN", # Unstructured covariance (estimates correlation)data = dat_long)
Warning: 184 rows with NAs omitted from model fitting.
summary(res)
Multivariate Meta-Analysis Model (k = 78; method: REML)
logLik Deviance AIC BIC AICc
-114.6704 229.3408 239.3408 250.9945 240.1980
Variance Components:
outer factor: study (nlvls = 39)
inner factor: outcome (nlvls = 2)
estim sqrt k.lvl fixed level
tau^2.1 0.6313 0.7946 39 no sens
tau^2.2 1.2396 1.1134 39 no spec
rho.sens rho.spec sens spec
sens 1 - 39
spec -0.2806 1 no -
Test for Residual Heterogeneity:
QE(df = 76) = 976.5509, p-val < .0001
Test of Moderators (coefficients 1:2):
QM(df = 2) = 171.0803, p-val < .0001
Model Results:
estimate se zval pval ci.lb ci.ub
outcomesens 1.4624 0.1463 9.9986 <.0001 1.1757 1.7490 ***
outcomespec 1.1010 0.1874 5.8761 <.0001 0.7338 1.4683 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
pred ci.lb ci.ub pi.lb pi.ub tau2.level
Sensitivity 0.8118957 0.7641767 0.851831 NA NA NA
Specificity 0.7504516 0.6756335 0.812794 NA NA NA
dat_sens <-subset(dat_long, outcome =="sens") k <-length(dat_sens$yi) # Automatically count the studies# 2. Create the forest plot with dynamic scalingmetafor::forest(dat_sens$yi, vi = dat_sens$vi, slab = dat_sens$study,transf = transf.ilogit, # ylim: bottom is -2 for the diamond, top is k+3 for headersylim =c(-2, k +3), rows =1:k,header ="Study",main ="Sensitivity")addpoly(res$beta[1], sei = res$se[1], row =-1, transf = transf.ilogit, mlab ="Pooled Sensitivity",col ="red")
dat_spec <-subset(dat_long, outcome =="spec") k <-length(dat_spec$vi) # Automatically count the studies# 2. Create the forest plot with dynamic scalingmetafor::forest(dat_spec$yi, vi = dat_spec$vi, slab = dat_spec$study,transf = transf.ilogit, # ylim: bottom is -2 for the diamond, top is k+3 for headersylim =c(-2, k +3), rows =1:k,header ="Study",main ="Specificity")addpoly(res$beta[2], sei = res$se[2], row =-1, transf = transf.ilogit, mlab ="Pooled Specificity",col ="red")
all_studies <- dat_long %>%group_by(study) %>%# Keep studies where at least one row (sens or spec) is not NAfilter(any(!is.na(yi))) %>%ungroup() %>%# Get the unique names and sort them alphabeticallydistinct(study) %>%arrange(desc(study)) %>%# Assign the fixed row numbers for the plotsmutate(row_num =row_number())# 2. Check the count (should be ~40 based on your current project)k_total <-nrow(all_studies)print(paste("Total studies in forest plot:", k_total))
[1] "Total studies in forest plot: 39"
# 1. Prepare data with dummy values and a color vectordat_sens_plot <- all_studies %>%left_join(filter(dat_long, outcome =="sens"), by ="study") %>%mutate(is_missing =is.na(yi),yi_plot =ifelse(is_missing, 0.5, yi), # Provide a dummy valuevi_plot =ifelse(is_missing, 0.001, vi), # Provide a tiny dummy variance# Create a color vector: "black" for real data, "white" for missingpt_col =ifelse(is_missing, "white", "black") ) %>% dplyr::arrange(instrument_self_report, study)dat_spec_plot <- all_studies %>%left_join(filter(dat_long, outcome =="spec"), by ="study") %>%mutate(is_missing =is.na(yi),yi_plot =ifelse(is_missing, 0.5, yi),vi_plot =ifelse(is_missing, 0.001, vi),pt_col =ifelse(is_missing, "white", "black") )%>% dplyr::arrange(instrument_self_report, study)# set text sizecex =0.6# 1. Define a layout where the first column is nearly twice as wide as the second# The widths = c(1.8, 1) ratio gives the left plot more 'breathing room'layout(matrix(c(1, 2), nrow =1), widths =c(1.5, 1))# 2. Adjust margins: Reduce the left margin of the Specificity plot (plot 2)# Sensitivity margins (standard)par(mar =c(5, 4, 3, 1)) # --- SENSITIVITY ---# Use a wider xlim to accommodate the text columnsxlim_sens <-c(-1, 1.5)metafor::forest(dat_sens_plot$yi_plot, vi = dat_sens_plot$vi_plot, slab = dat_sens_plot$study,transf = transf.ilogit, xlim = xlim_sens, ylim =c(-2, k_total +3), rows = all_studies$row_num,col = dat_sens_plot$pt_col,header ="Study", main ="Sensitivity", refline =NA,cex = cex)text(-0.5, k_total +2, "Instrument", pos =4, cex = cex, font =2) text(-0.5, all_studies$row_num, dat_sens_plot$instrument_self_report, pos =4, cex = cex)addpoly(res$beta[1], sei = res$se[1], row =-1, transf = transf.ilogit, mlab ="", col ="red", cex = cex)# 3. Switch to the second plot and tighten its marginspar(mar =c(5, 1, 3, 2)) # --- SPECIFICITY ---# Use a much tighter xlim since there are no labels on the leftxlim_spec <-c(-0.5, 1.5)metafor::forest(dat_spec_plot$yi_plot, vi = dat_spec_plot$vi_plot, slab =NA, transf = transf.ilogit, xlim = xlim_spec,ylim =c(-2, k_total +3), rows = all_studies$row_num,col = dat_spec_plot$pt_col,header ="", main ="Specificity", refline =NA,cex = cex)addpoly(res$beta[2], sei = res$se[2], row =-1, transf = transf.ilogit, mlab ="", col ="red", cex = cex)